package com.freecloud.bitorrent.domain.server.codec;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.freecloud.bitorrent.common.util.ByteUtil;
import com.freecloud.bitorrent.common.util.bencode.BencodingUtils;
import com.freecloud.bitorrent.constant.BitTorrentConstant;
import com.freecloud.bitorrent.demo.bo.Arguments;
import com.freecloud.bitorrent.demo.bo.BitTorrentMessage;
import com.freecloud.bitorrent.demo.bo.FindNodeRequest;
import com.freecloud.bitorrent.demo.bo.RequestMessage;
import com.freecloud.bitorrent.enums.MessageTypeEnum;
import com.freecloud.bitorrent.enums.QueryEnum;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageDecoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

/**
 * @Author: maomao
 * @Date: 2022/9/8 11:33 AM
 */
@Slf4j
@Component
public class BitTorrentDecoder extends MessageToMessageDecoder<DatagramPacket> {

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        log.error("BitTorrentDecoder error : {}", cause);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, DatagramPacket datagramPacket, List<Object> list)
        throws Exception {
        log.info("bitTorrentDecorder:{}", datagramPacket);
        byte[] buff = new byte[datagramPacket.content().readableBytes()];
        datagramPacket.content().readBytes(buff);
        ctx.executor().execute(() -> {
            Map<String, ?> msg = BencodingUtils.decode(buff);
            log.info("DhtDecoder ： {}", msg);
            if (CollUtil.isEmpty(msg)) {
                log.warn("消息为空，无法处理：{}", msg);
                return;
            }
            byte[] messageType = (byte[])msg.get(BitTorrentConstant.DHT_MESSAGE_TYPE);
            if (messageType == null) {
                log.warn("messageType为空，无法处理：{}", msg);
                return;
            }
            String messageTypeStr = new String(messageType);
            if (StrUtil.isBlank(messageTypeStr)) {
                log.warn("messageType为空字符串，无法处理：{}", msg);
                return;
            }
            try {
                MessageTypeEnum messageTypeEnum = MessageTypeEnum.getQueryEnum(messageTypeStr);
                if (MessageTypeEnum.Error == messageTypeEnum) {
                    log.warn("messageType error，无法处理：{}", msg);
                    return;
                } else {
                    list.add(doRequest(messageTypeEnum, msg));
                }
            } catch (Exception e) {
                log.warn("解析数据错误：{}", e);
            }
        });
        log.info("List : {}", list);
    }

    private BitTorrentMessage doRequest(MessageTypeEnum messageTypeEnum, Map<String, ?> msg) {
        QueryEnum queryEnum = null;
        RequestMessage requestMessage = new RequestMessage();
        requestMessage.setMessageType(MessageTypeEnum.Query);
        requestMessage.setTransactionId(new String((byte[])msg.get(BitTorrentConstant.DHT_MESSAGE_TRANSACTION_ID)));
        if (msg.containsKey(BitTorrentConstant.DHT_MESSAGE_QUERY)) {
            queryEnum = QueryEnum.getQueryEnum(new String((byte[])msg.get(BitTorrentConstant.DHT_MESSAGE_QUERY)));
        } else if (MessageTypeEnum.Return.equals(messageTypeEnum)) {
            queryEnum = QueryEnum.getQueryEnum(requestMessage.getTransactionId());
        }
        requestMessage.setQuery(queryEnum);

        if (msg.containsKey(BitTorrentConstant.DHT_MESSAGE_VERSION)) {
            requestMessage.setVersion(new String((byte[])msg.get(BitTorrentConstant.DHT_MESSAGE_VERSION)));
        }
        if (msg.containsKey(BitTorrentConstant.DHT_MESSAGE_ARGUMENTS)) {
            Map<String, byte[]> argsMap = (Map<String, byte[]>)msg.get(BitTorrentConstant.DHT_MESSAGE_ARGUMENTS);
            requestMessage.setArguments(getArguments(requestMessage, argsMap));
        }
        return requestMessage;
    }

    private Arguments getArguments(RequestMessage requestMessage, Map<String, byte[]> argsMap) {
        log.info("arguments : {}", argsMap);
        Arguments arguments = null;
        switch (requestMessage.getQuery()) {
            case FindNode:
                arguments = getFindNodeRequest(argsMap);
        }
        return arguments;
    }

    private FindNodeRequest getFindNodeRequest(Map<String, byte[]> argsMap) {
        FindNodeRequest findNodeRequest = new FindNodeRequest();
        findNodeRequest.setId(ByteUtil.byteArrayToHex(argsMap.get(BitTorrentConstant.DHT_MESSAGE_ID)));
        findNodeRequest.setTarget(ByteUtil.byteArrayToHex(argsMap.get(BitTorrentConstant.DHT_ARGUMENTS_TARGET)));
        return findNodeRequest;
    }
}
